package com.bingxue.edu.management.shop.service;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.bingxue.edu.common.config.FuiouPayProperties;
import com.bingxue.edu.common.enums.ChangeTypeEnum;
import com.bingxue.edu.common.enums.EnrollStatusEnum;
import com.bingxue.edu.common.enums.JoinTypeEnum;
import com.bingxue.edu.common.enums.OperateSourceEnum;
import com.bingxue.edu.common.enums.OperateStatusEnum;
import com.bingxue.edu.common.enums.OrderStatusEnum;
import com.bingxue.edu.common.enums.PayTypeEnum;
import com.bingxue.edu.common.util.EduUtil;
import com.bingxue.edu.framework.common.constant.CacheConstants;
import com.bingxue.edu.framework.common.model.PageQuery;
import com.bingxue.edu.framework.common.util.ValidationUtils;
import com.bingxue.edu.framework.excel.model.OrderExportModel;
import com.bingxue.edu.framework.exception.BusinessException;
import com.bingxue.edu.framework.mybatis.base.BaseService;
import com.bingxue.edu.management.education.model.entity.ClassInfo;
import com.bingxue.edu.management.education.model.entity.EnrollChangeRecord;
import com.bingxue.edu.management.education.model.entity.Enrollment;
import com.bingxue.edu.management.education.model.entity.LessonStudent;
import com.bingxue.edu.management.education.model.entity.Student;
import com.bingxue.edu.management.education.service.ClassInfoService;
import com.bingxue.edu.management.education.service.EnrollPermissionService;
import com.bingxue.edu.management.education.service.EnrollRenewalService;
import com.bingxue.edu.management.education.service.EnrollmentService;
import com.bingxue.edu.management.education.service.LessonService;
import com.bingxue.edu.management.education.service.LessonStudentService;
import com.bingxue.edu.management.education.service.StudentService;
import com.bingxue.edu.management.shop.mapper.OrderMapper;
import com.bingxue.edu.management.shop.model.entity.Coupon;
import com.bingxue.edu.management.shop.model.entity.Order;
import com.bingxue.edu.management.shop.model.entity.OrderItem;
import com.bingxue.edu.management.shop.model.query.OrderQuery;
import com.bingxue.edu.management.shop.model.query.OrderRecentStatQuery;
import com.bingxue.edu.management.shop.model.query.PayResultQuery;
import com.bingxue.edu.management.shop.model.req.CouponUseReq;
import com.bingxue.edu.management.shop.model.req.FuiouPayOrderCreateReq;
import com.bingxue.edu.management.shop.model.req.OrderCreateReq;
import com.bingxue.edu.management.shop.model.req.OrderEndValidTimeUpdateReq;
import com.bingxue.edu.management.shop.model.req.OrderItemCreateReq;
import com.bingxue.edu.management.shop.model.req.OrderOnlinePayReq;
import com.bingxue.edu.management.shop.model.req.OrderPayCompleteReq;
import com.bingxue.edu.management.shop.model.req.OrderPayTypeUpdateReq;
import com.bingxue.edu.management.shop.model.req.OrderPriceCalculateReq;
import com.bingxue.edu.management.shop.model.resp.CouponDetail;
import com.bingxue.edu.management.shop.model.resp.OrderDetailResp;
import com.bingxue.edu.management.shop.model.resp.OrderPriceDetail;
import com.bingxue.edu.management.shop.model.resp.OrderRecentStatResp;
import com.bingxue.edu.management.shop.model.resp.OrderResp;
import com.bingxue.edu.management.shop.model.resp.OrderStatResp;
import com.bingxue.edu.parent.model.req.ParentCartItemAddReq;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Row;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Date;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.bingxue.edu.common.util.EduUtil.generateOrderId;
import static com.bingxue.edu.management.education.model.entity.table.StudentTableDef.STUDENT;
import static com.bingxue.edu.management.shop.model.entity.table.OrderTableDef.ORDER;

/**
 * 购课订单业务实现类
 *
 * @author panbo
 * @since 2024/08/27 14:31
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService extends BaseService<OrderMapper, Order> {

    private final OrderItemService orderItemService;
    private final StudentService studentService;
    private final ClassInfoService classInfoService;
    private final EnrollPermissionService enrollPermissionService;
    private final EnrollmentService enrollmentService;
    private final EnrollRenewalService enrollRenewalService;
    private final CouponService couponService;
    private final FuiouPayProperties fuiouPayProperties;
    private final FuiouPayService fuiouPayService;
    private final LessonService lessonService;
    private final CartService cartService;
    private final LessonStudentService lessonStudentService;

    private final RedissonClient redissonClient;

    /**
     * 创建订单
     */
    @Transactional(rollbackFor = Exception.class)
    public String createOrder(OrderCreateReq req) {
        OrderPriceDetail priceDetail = calculatePrice(req);

        for (OrderItem orderItem : priceDetail.getOrderItemList()) {
            ClassInfo classInfo = orderItem.getClassInfo();
            ValidationUtils.throwIfNull(classInfo, "班级不存在");
            EduUtil.checkClassStatus(classInfo);
            // 判断是否存在相同班级且未支付的订单，如果存在则报错
            if(orderItemService.exitsPendingPayOrderItemByClassId(req.getStudentId(), classInfo.getId())) {
                throw new BusinessException("已存在相同班级的待支付订单，请勿重复报名");
            }
            // 前台操作进行判断
            if (req.getOperateSource() != OperateSourceEnum.BACKEND) {
                // - 判断班级名额
                EduUtil.checkAvailableCount(classInfo);
                // - 判断选课时间是否满足要求
                EduUtil.checkEnrollTime(classInfo);
                // - 判断选课权限是否满足要求
                // 不判断，只在加入购物车时判断
//                enrollPermissionService.checkPermission(req.getStudentId(), classInfo);
            }
        }
        // 创建订单
        Order order = initNewOrder(req);
        order.setOriginalTotalPrice(priceDetail.getOriginalTotalPrice());
        order.setActualTotalPrice(priceDetail.getActualTotalPrice());
        order.addDiscountDetail("优惠总额", priceDetail.getDiscountTotalPrice());
        order.addDiscountDetail("优惠券优惠", priceDetail.getCouponDiscountPrice());
        order.addDiscountDetail("联报优惠", priceDetail.getPackageDiscountPrice());
        order.addDiscountDetail("其它优惠", priceDetail.getOtherDiscountPrice());
        this.save(order);

        // 核销优惠券
        for (CouponDetail coupon : priceDetail.getCouponDetailList()) {
            CouponUseReq couponUseReq = new CouponUseReq();
            couponUseReq.setId(coupon.getId());
            couponUseReq.setCouponTemplateId(coupon.getCouponTemplateId());
            couponUseReq.setOrderId(order.getId());
            couponService.useCoupon(couponUseReq);
        }

        for (OrderItem orderItem : priceDetail.getOrderItemList()) {
            orderItem.setOrderId(order.getId());
            Enrollment enrollment = enrollmentService.getStudentEnrollment(orderItem.getClassId(), req.getStudentId());
            if (enrollment == null) {
                // 如果没有报名信息，则增加班级报名人数，实现占用名额
                classInfoService.increaseEnrollStudentCount(orderItem.getClassId(), 1);
            } else {
                // 如果有报名信息，判断是否存在同样的课次
                List<LessonStudent> existLessonStudent = lessonStudentService.getListByLessonIndex(enrollment.getId(), orderItem.getLessonIndexes());
                List<Integer> existLessonIndexList = existLessonStudent.stream().map(LessonStudent::getLessonIndex).toList();
                ValidationUtils.throwIfNotEmpty(existLessonStudent, "不能重复报名第[{}]次课", Convert.toStr(existLessonIndexList));
            }
        }
        // 创建order item
        orderItemService.saveBatch(priceDetail.getOrderItemList());
        // 删除购物车
        for (OrderItem orderItem : priceDetail.getOrderItemList()) {
            cartService.deleteItemByStudentAndClassId(orderItem.getStudentId(), orderItem.getClassId());
        }
        log.info("创建订单成功, {}", req);
        return order.getId();
    }

    /**
     * 发起在线支付
     */
    @Transactional(rollbackFor = Exception.class)
    public JSONObject onlinePay(OrderOnlinePayReq req) {
        RLock lock = redissonClient.getLock(CacheConstants.ORDER_PAY_LOCK_KEY + req.getOrderId());
        try {
            lock.lock(30, TimeUnit.SECONDS);
            Order order = getById(req.getOrderId());
            ValidationUtils.throwIfNull(order, "订单不存在");
            ValidationUtils.throwIf(order.getStatus() == OrderStatusEnum.PAID || order.getPayOff(), "订单已支付");
            ValidationUtils.throwIf(order.getStatus() == OrderStatusEnum.CANCELED || order.isCancel(), "订单已取消");
            // 查询已经创建过的支付订单
            if (order.getStatus() == OrderStatusEnum.PAYING || order.getPayOrderDetail() != null) {
                ValidationUtils.throwIf(isOnlinePaid(order), "请勿重复支付");

                JSONObject payDetail = new JSONObject(order.getPayOrderDetail());
                JSONObject orderInfo = payDetail.getJSONObject("order_info");
                String timeStamp = orderInfo.getStr("timeStamp");
                LocalDateTime createPayOrderTime = Instant.ofEpochSecond(Integer.parseInt(timeStamp)).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
                if (LocalDateTime.now().isAfter(createPayOrderTime.plusMinutes(fuiouPayProperties.getOrderTimeout()))) {
                    log.warn("订单预支付信息已超时, orderId: {}, timeStamp: {}, now: {}", order.getId(), timeStamp, createPayOrderTime);
                } else {
                    log.info("存在已创建的预支付信息, orderId: {}", order.getId());
                    return payDetail.getJSONObject("order_info");
                }
            }
            // 如果未发起过线上支付，或线上支付超时，重新发起支付
            FuiouPayOrderCreateReq payOrderCreateReq = new FuiouPayOrderCreateReq();
            payOrderCreateReq.setOrderId(req.getOrderId());
            payOrderCreateReq.setOrderDate(order.getCreateTime());
            payOrderCreateReq.setOpenid(req.getOpenid());
            payOrderCreateReq.setOrderAmount(order.getActualTotalPrice());
            payOrderCreateReq.setGoodsName("报名费用");
            JSONObject payOrderResult = fuiouPayService.createPayOrder(payOrderCreateReq);
            // {"appId":"wxd678efh567hg6787","timeStamp":"1414561699","nonceStr":"5K8264ILTKCH16CQ2502SI8ZNMTM67VS","package":"prepay_id=123456789","signType":"RSA","paySign":"C380BEC2BFD727A4B6845133519F3AD6"}
            // 使用返回的支付数据，调用微信官方接口 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
            order.setOutTradeNo(payOrderResult.getStr("order_id"));
            order.setPayOrderDetail(payOrderResult.toString());
            order.setStatus(OrderStatusEnum.PAYING);
            this.updateById(order);
            return payOrderResult.getJSONObject("order_info");
        } catch (Exception e) {
            log.warn("发起线上支付失败: {}", e.getMessage());
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 取消订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(String orderId) {
        Order order = getById(orderId);
        ValidationUtils.throwIfNull(order, "订单不存在");
        ValidationUtils.throwIf(order.getPayOff(), "订单已支付");
        if (order.getStatus() == OrderStatusEnum.CANCELED || order.isCancel()) {
            return;
        }
        order.setStatus(OrderStatusEnum.CANCELED);
        order.setCancelTime(LocalDateTime.now());
        this.updateById(order);
        Student student = studentService.getById(order.getStudentId());
        // 撤回优惠券
        List<Coupon> couponList = couponService.getParentCouponByOrderId(student.getParentId(), order.getId());
        for (Coupon coupon : couponList) {
            CouponUseReq couponUseReq = new CouponUseReq();
            couponUseReq.setId(coupon.getId());
            couponUseReq.setCouponTemplateId(coupon.getCouponTemplateId());
            couponService.rollbackCoupon(couponUseReq);
        }
        // 释放班级名额
        List<OrderItem> orderItemList = orderItemService.getListByOrderId(orderId);
        // 过滤班级id
        List<String> classIds = orderItemList.stream().map(OrderItem::getClassId).distinct().toList();
        for (String classId : classIds) {
            Enrollment enrollment = enrollmentService.getStudentEnrollment(classId, order.getStudentId());
            // 如果没有报名信息，则减少班级报名人数，释放名额。
            if (enrollment == null) {
                classInfoService.increaseEnrollStudentCount(classId, -1);
            }
        }
        // 班级添加回购物车
        for (OrderItem orderItem : orderItemList) {
            ParentCartItemAddReq req = new ParentCartItemAddReq();
            req.setClassId(orderItem.getClassId());
            req.setStudentId(order.getStudentId());
            req.setLessonIndexes(orderItem.getLessonIndexes());
            try {
                cartService.addCartItem(req, false);
            }  catch (Exception e){
                log.warn("班级添加回购物车失败: {}", req, e);
            }
        }

        log.info("订单取消成功, {}", orderId);
    }

    /**
     * 完成订单
     * 加锁执行，保证同一时间只能有一个请求执行。
     */
    @Transactional(rollbackFor = Exception.class)
    public void completeOrder(OrderPayCompleteReq req) {
        if (req.getOrderId() == null) {
            return;
        }
        RLock lock = redissonClient.getLock(CacheConstants.ORDER_COMPLETE_LOCK_KEY + req.getOrderId());
        try {
            // 最多锁定30秒
            lock.lock(30, TimeUnit.SECONDS);
            Order order = getById(req.getOrderId());
            ValidationUtils.throwIfNull(order, "订单不存在");
            ValidationUtils.throwIf(order.getStatus() == OrderStatusEnum.CANCELED, "订单已取消");
            ValidationUtils.throwIf(order.getStatus() == OrderStatusEnum.PAID, "订单已完成支付");
            // deprecated
            ValidationUtils.throwIf(order.getPayOff(), "订单已完成支付");

            order.setStatus(OrderStatusEnum.PAID);
            order.setPayOff(true);
            order.setPayType(req.getPayType());
            order.setFinAccountId(req.getFinAccountId());
            order.setPayTime(req.getPayTime() == null ? LocalDateTime.now() : req.getPayTime());
            order.setOutTradeNo(req.getOutTradeNo());
            this.updateById(order);

            // 订单生效
            List<OrderItem> orderItemList = orderItemService.getListByOrderId(req.getOrderId());
            for (OrderItem orderItem : orderItemList) {
                ClassInfo classInfo = classInfoService.getById(orderItem.getClassId());
                // 先查询是否存在报名记录
                Enrollment enrollment = enrollmentService.getStudentEnrollment(orderItem.getClassId(), order.getStudentId());
                if (enrollment == null) {
                    enrollment = enrollmentService.initEnrollment(order, orderItem, classInfo);
                    EnrollChangeRecord record = new EnrollChangeRecord();
                    record.setStudentId(enrollment.getStudentId());
                    record.setChangeType(ChangeTypeEnum.NEW_ENROLL);
                    record.setBusinessId(orderItem.getId());
                    record.setOriginalClassId(orderItem.getClassId());
                    record.setOriginalLessonIndexes(orderItem.getLessonIndexes());
                    record.setOperateStatus(OperateStatusEnum.SUCCESS);
                    record.setOperateSource(req.getOperateSource());
                    String details = "报名[%s], 购课课次为第%s次课, 共%d次课".formatted(classInfo.getName(), orderItem.getLessonIndexes(), orderItem.getLessonIndexes().size());
                    record.setDetails(details);
                    // 更新报名信息
                    enrollmentService.updateEnrollment(enrollment, record);
                    // 生成班级学员
                    classInfoService.addStudentToClass(enrollment.getStudentId(), enrollment.getClassId(), enrollment.getId(), JoinTypeEnum.ENROLL);
                    // 生成课次学员
                    lessonService.addLessonToStudent(orderItem, enrollment.getStudentId(), classInfo, enrollment.getId(), JoinTypeEnum.ENROLL);
                    // 自动更新权限
                    enrollPermissionService.updatePermission(order.getStudentId(), classInfo);
                    // 更新续报状态
                    enrollRenewalService.updateRenewalStatus(enrollment, classInfo);
                } else {
                    // 存在则更新
                    enrollment.setEnrollLessonCount(enrollment.getEnrollLessonCount() + orderItem.getLessonIndexes().size());
                    EnrollChangeRecord record = new EnrollChangeRecord();
                    record.setStudentId(enrollment.getStudentId());
                    record.setChangeType(ChangeTypeEnum.ADD_LESSON);
                    record.setEnrollmentId(enrollment.getId());
                    record.setBusinessId(orderItem.getId());
                    record.setOriginalClassId(orderItem.getClassId());
                    record.setOriginalLessonIndexes(orderItem.getLessonIndexes());
                    record.setOperateStatus(OperateStatusEnum.SUCCESS);
                    record.setOperateSource(req.getOperateSource());
                    String details = "续费[%s], 购课课次为第%s次课, 共%d次课".formatted(classInfo.getName(), orderItem.getLessonIndexes(), orderItem.getLessonIndexes().size());
                    record.setDetails(details);
                    // 更新报名信息
                    enrollmentService.updateEnrollment(enrollment, record);
                    // 生成课次学员
                    lessonService.addLessonToStudent(orderItem, enrollment.getStudentId(), classInfo, enrollment.getId(), JoinTypeEnum.ENROLL);
                    // 更新续报状态
                    enrollRenewalService.updateRenewalStatus(enrollment, classInfo);
                }
            }
            // 更新学生状态
            studentService.updateStudentStatus(order.getStudentId(), EnrollStatusEnum.ON_STUDY);
            log.info("完成支付成功, req: {}", req);
        } catch (Exception e){
            log.warn("完成支付失败, req: {}, error: {}", req, e.getMessage());
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 完成支付
     */
    @Transactional(rollbackFor = Exception.class)
    public void completeOnlinePay(JSONObject message) {
        String orderId = message.getStr("order_id");
        String orderSt = message.getStr("order_st");
        String ourTradeNo = message.getStr("fy_order_id");
        if (!orderSt.equals("1")) {
            log.info("支付回调跳过, 支付失败或未支付, orderId: {}", orderId);
            return;
        }
        OrderPayCompleteReq req = new OrderPayCompleteReq();
        req.setOrderId(orderId);
        req.setFinAccountId(PayTypeEnum.FUIOU.getValue());
        req.setPayType(PayTypeEnum.FUIOU);
        req.setPayTime(LocalDateTime.now());
        req.setOutTradeNo(ourTradeNo);
        req.setOperateSource(OperateSourceEnum.PARENT);
        completeOrder(req);
        log.info("支付回调完成, orderId: {}", orderId);
    }

    /**
     * 查询线上支付结果
     */
    public boolean isOnlinePaid(Order order) {
        PayResultQuery query = new PayResultQuery();
        query.setOrderId(order.getId());
        query.setOrderDate(order.getCreateTime());
        JSONObject message = fuiouPayService.queryPayResult(query);
        String orderSt = message.getStr("order_st");
        return orderSt.equals("1");
    }

    /**
     * 刷新支付结果
     */
    @Transactional(rollbackFor = Exception.class)
    public void refreshPayResult(Order order) {
        ValidationUtils.throwIfNull(order, "订单不存在");
//        if(order.getPayType() != PayTypeEnum.FUIOU) {
//            log.info("订单为线下支付, 跳过刷新, {}", order.getId());
//            return;
//        }
        if (order.getStatus() == OrderStatusEnum.PAID || order.getPayOff()) {
            log.info("订单已完成支付, {}", order.getId());
            return;
        }
        if (order.getStatus() == OrderStatusEnum.CANCELED || order.isCancel()) {
            log.info("订单已取消, {}", order.getId());
            return;
        }
        PayResultQuery query = new PayResultQuery();
        query.setOrderId(order.getId());
        query.setOrderDate(order.getCreateTime());
        JSONObject message = fuiouPayService.queryPayResult(query);
        log.info("查询支付结果, {}", message);
        completeOnlinePay(message);
    }

    public Page<OrderResp> getPage(OrderQuery query, PageQuery pageQuery) {
        QueryWrapper queryWrapper = buildQueryWrapper(query);
        return this.getMapper().paginateWithRelationsAs(pageQuery.toPage(), queryWrapper, OrderResp.class);
    }

    public OrderDetailResp getDetailById(String id) {
        // 获取订单信息
        return this.getMapper().selectOneWithRelationsByIdAs(id, OrderDetailResp.class);
    }

    public OrderStatResp getOrderStat(OrderQuery query) {
//        // 今日待缴费人数
//        long pendingPayCount = this.queryChain().where(ORDER.CREATE_TIME.ge(LocalDate.now()))
//                .and(ORDER.STATUS.in(OrderStatusEnum.PENDING, OrderStatusEnum.PAYING))
//                .count();
        // 订单总数
        QueryWrapper queryWrapper = buildQueryWrapper(query).select(
                QueryMethods.sum(ORDER.ACTUAL_TOTAL_PRICE).as(OrderStatResp::getTotalOrderAmount),
                QueryMethods.count(ORDER.ID).as(OrderStatResp::getTotalOrderCount)
        ).where(ORDER.STATUS.eq(OrderStatusEnum.PAID));
        OrderStatResp orderStatResp = this.getOneAs(queryWrapper, OrderStatResp.class);
        // 今日订单数
        long todayOrderCount = this.queryChain()
                .where(ORDER.PAY_TIME.ge(LocalDate.now()))
                    .and(ORDER.STATUS.eq(OrderStatusEnum.PAID))
                .count();
        orderStatResp.setTodayOrderCount(todayOrderCount);

//        orderStatResp.setPendingPayCount(pendingPayCount);
        return orderStatResp;
    }

    public OrderRecentStatResp getOrderRecentStat(OrderRecentStatQuery query) {
        OrderRecentStatResp resp = new OrderRecentStatResp();
        Map<LocalDate, Integer> orderCountResult = new HashMap<>();
        Map<LocalDate, BigDecimal> orderPriceResult = new HashMap<>();
        for(LocalDate date = query.getDateFrom(); !date.isAfter(query.getDateTo()); date = date.plusDays(1)) {
            orderCountResult.put(date, 0);
            orderPriceResult.put(date, BigDecimal.ZERO);
        }
        QueryWrapper queryWrapper = this.query()
                .select(
                        QueryMethods.date(ORDER.PAY_TIME).as("date"),
                        QueryMethods.count().as("orderCount"),
                        QueryMethods.sum(ORDER.ACTUAL_TOTAL_PRICE).as("orderPrice")
                )
                .from(ORDER)
                .where(ORDER.STATUS.eq(OrderStatusEnum.PAID.getValue()))
                .and(ORDER.PAY_TIME.ge(query.getDateFrom()))
                .and(ORDER.PAY_TIME.lt(query.getDateTo()))
                .groupBy(QueryMethods.date(ORDER.PAY_TIME));
        List<Row> rows = this.getMapper().selectRowsByQuery(queryWrapper);
        for (Row row : rows) {
            LocalDate localDate = ((Date)row.get("date")).toLocalDate();
            orderCountResult.put(localDate, row.getInt("orderCount"));
            orderPriceResult.put(localDate, row.getBigDecimal("orderPrice"));
        }

        resp.setOrderCountStat(orderCountResult);
        resp.setOrderPriceStat(orderPriceResult);

        return resp;
    }

    private QueryWrapper buildQueryWrapper(OrderQuery query) {
        return QueryWrapper.create()
                .from(ORDER)
                .where(ORDER.ID.eq(query.getOrderId()))
                .and(ORDER.STATUS.in(query.getStatus()))
                .and(ORDER.PAY_TYPE.eq(query.getPayType()))
                .and(ORDER.CREATE_TIME.ge(query.getCreateDateFrom()))
                .and(ORDER.CREATE_TIME.lt(query.getCreateDateTo()))
                .and(ORDER.PAY_TIME.ge(query.getPayDateFrom()))
                .and(ORDER.PAY_TIME.lt(query.getPayDateTo()))
                .and(ORDER.STUDENT_ID.eq(query.getStudentId()))
                .and(ORDER.FIN_ACCOUNT_ID.eq(query.getFinAccountId()))
                .orderBy(ORDER.ID.desc());
    }

    /**
     * 计算订单价格
     */
    public OrderPriceDetail calculatePrice(OrderPriceCalculateReq req) {
        OrderPriceDetail orderPriceDetail = new OrderPriceDetail();

        Student student = studentService.getById(req.getStudentId());
        ValidationUtils.throwIfNull(student, "学员信息不存在");

        // 同一笔订单不能有相同班级
        List<OrderItemCreateReq> orderItemReqList = req.getOrderItemList();
        ValidationUtils.throwIfEmpty(orderItemReqList, "报名班级不能为空");
        Set<String> classIdSet = orderItemReqList.stream().map(OrderItemCreateReq::getClassId).collect(Collectors.toSet());
        ValidationUtils.throwIfNotEqual(orderItemReqList.size(), classIdSet.size(), "购课订单不能包含重复班级");

        // 订单购课项
        List<OrderItem> orderItemList = new ArrayList<>();
        // 订单购课班级
        for (OrderItemCreateReq orderItemReq : orderItemReqList) {
            List<Integer> lessonIndexes = orderItemReq.getLessonIndexes();
            ValidationUtils.throwIfEmpty(lessonIndexes, "课次列表不能为空");
            ValidationUtils.throwIfNotEqual(lessonIndexes.size(), new HashSet<>(lessonIndexes).size(), "课次列表不能重复");

            ClassInfo classInfo = classInfoService.getById(orderItemReq.getClassId());
            ValidationUtils.throwIfNull(classInfo, "班级不存在");

            OrderItem orderItem = new OrderItem();
            orderItem.setClassId(classInfo.getId());
            orderItem.setClassName(classInfo.getName());
            orderItem.setStudentId(student.getId());
            orderItem.setLessonIndexes(lessonIndexes);
            orderItem.setClassInfo(classInfo);
            orderItemList.add(orderItem);
        }

        BigDecimal originalTotalPrice = new BigDecimal(0); // 订单总价
        BigDecimal actualTotalPrice = new BigDecimal(0); // 实际总价
        // 计算原价
        for (OrderItem orderItem : orderItemList) {
            ClassInfo classInfo = orderItem.getClassInfo();
            BigDecimal singleClassPrice = classInfo.getUnitPrice().multiply(BigDecimal.valueOf(orderItem.getLessonIndexes().size()));
            orderItem.setActualUnitPrice(classInfo.getUnitPrice());
            orderItem.setActualTotalPrice(singleClassPrice);

            originalTotalPrice = originalTotalPrice.add(singleClassPrice);
            actualTotalPrice = actualTotalPrice.add(singleClassPrice);
        }
        BigDecimal discountTotalPrice = new BigDecimal(0); // 订单优惠总金额
        BigDecimal couponDiscountPrice = new BigDecimal(0);  // 优惠券优惠金额
        BigDecimal packageDiscountPrice = new BigDecimal(0); // 联报优惠金额
        BigDecimal otherDiscountPrice = new BigDecimal(0); // 其他优惠金额
        // 计算优惠券优惠
        List<CouponDetail> couponList = couponService.getParentCouponDetailByIdList(student.getParentId(), req.getCouponIdList());
        if(!couponList.isEmpty()) {
            // 优惠券排序，先满减再折扣
            couponList.sort(Comparator.comparingInt(o -> o.getTemplate().getDiscountType().getPriority()));
            // 校验优惠券是否可用
            couponService.checkCouponValid(couponList);
            // 计算折扣金额
            for (CouponDetail coupon : couponList) {
                couponDiscountPrice = couponDiscountPrice.add(couponService.calculateDiscount(coupon.getTemplate(), orderItemList));
            }
            // 计算实际价格
            discountTotalPrice = discountTotalPrice.add(couponDiscountPrice);
            discountTotalPrice = discountTotalPrice.add(packageDiscountPrice);
            discountTotalPrice = discountTotalPrice.add(otherDiscountPrice);
            actualTotalPrice = originalTotalPrice.subtract(discountTotalPrice);
            ValidationUtils.throwIf(actualTotalPrice.compareTo(BigDecimal.ZERO) < 0, "订单总价不能小于0");
        }

        orderPriceDetail.setStudent(student);
        orderPriceDetail.setCouponDetailList(couponList);
        orderPriceDetail.setOrderItemList(orderItemList);
        orderPriceDetail.setOriginalTotalPrice(originalTotalPrice);
        orderPriceDetail.setActualTotalPrice(actualTotalPrice);
        orderPriceDetail.setDiscountTotalPrice(discountTotalPrice);
        orderPriceDetail.setCouponDiscountPrice(couponDiscountPrice);
        orderPriceDetail.setPackageDiscountPrice(packageDiscountPrice);
        orderPriceDetail.setOtherDiscountPrice(otherDiscountPrice);

        return orderPriceDetail;
    }

    /**
     * 获取超时未支付订单
     */
    public List<Order> getTimeoutPendingPayOrders() {
        return this.queryChain()
                .where(ORDER.STATUS.in(OrderStatusEnum.PENDING, OrderStatusEnum.PAYING))
                .and(ORDER.END_VALID_TIME.le(LocalDateTime.now()))
                .list();
    }

    /**
     * 更新订单过期时间
     * @param req 修改订单过期时间实体
     */
    public void updateEndValidTime(OrderEndValidTimeUpdateReq req) {
        this.updateChain()
                .set(ORDER.END_VALID_TIME,req.getEndValidTime())
                .where(ORDER.ID.eq(req.getOrderId()))
                .and(ORDER.STATUS.in(OrderStatusEnum.PENDING,OrderStatusEnum.PAYING))
                .update();

        log.info("更新订单过期时间成功: {}", req);
    }

    public void updatePayType(OrderPayTypeUpdateReq req) {
        this.updateChain()
                .set(ORDER.PAY_TIME,req.getPayTime())
                .set(ORDER.FIN_ACCOUNT_ID,req.getFinAccountId())
                .where(ORDER.ID.eq(req.getOrderId()))
                .update();
        log.info("更新订单支付方式成功: {}", req);
    }

    /**
     * 初始化订单
     */
    private Order initNewOrder(OrderCreateReq req) {
        Order order = new Order();
        order.setId(generateOrderId());
        order.setStudentId(req.getStudentId());
        order.setPayType(req.getPayType());
        order.setStatus(OrderStatusEnum.PENDING);
        order.setPayOff(false);
//        order.setRefunded(false);
        order.setRemark(req.getRemark());
        order.setRemarkOut(req.getRemarkOut());
        order.setOperateSource(req.getOperateSource());
        order.setEndValidTime(LocalDateTime.now().plusDays(1));
        return order;
    }


    /**
     * 导出订单列表
     */
    public void exportOrderList(HttpServletResponse response) throws IOException {
        List<OrderExportModel> orderExportModels = this.getExportOrderModelList();

        response.setContentType("application/x-msdownload");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("订单列表.xlsx"));
        // response.addHeader("Content-Length", String.valueOf(outputStream.available()));
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

        // 创建excel对象
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), OrderExportModel.class).build();

        //创建sheet
        WriteSheet writeSheet=EasyExcel.writerSheet("订单列表").head(OrderExportModel.class).build();

        excelWriter.write(orderExportModels, writeSheet);

        excelWriter.finish();
    }


    /**
     * 查询订单列表导出模型信息
     */
    private List<OrderExportModel> getExportOrderModelList() {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select(
                        ORDER.ALL_COLUMNS,
                        STUDENT.NAME.as(OrderExportModel::getStudentName)
                )
                .from(ORDER)
                .leftJoin(STUDENT).on(ORDER.STUDENT_ID.eq(STUDENT.ID));
        return this.getMapper().selectListWithRelationsByQueryAs(queryWrapper,OrderExportModel.class);
    }

}
